import util
import yaml
import stackless
from copy import copy
from action import Action

class Object:
    _objects = {}
    
    def __init__(self, name, **data):
        self.name = None
        if data.get('fromServer',False):
            self.name = name
            data.pop('fromServer',None)
        else:
            self.name = networkServer.makeObjectName(name)
        Object._objects[self.name] = self
        self.actions = {}
        
        self.alive = True
        
        self.vars = {}
        self.dirtyVars = set()
        
        self.loadFunctions = util.getFunctions(self,"load_")
        
        self.loadArgs(data)
        
    def load(self, filename):
        from fileServer import fileServer
        self.loadArgs(yaml.evalLoad(open(fileServer.filePaths[filename])))
        
    def loadArgs(self, data):
        for key, val in data.iteritems():
            assert key in self.loadFunctions, "unknown object data key %s in object %s" % (key, self.name)
            self.loadFunctions[key](val)
            
    def __repr__(self):
        return "<Object %s>" % self.name
    
    def getVarDirty(self, name):
        return name in self.dirtyVars
    
    def setVarDirty(self, name, dirty):
        if dirty and name not in self.dirtyVars:
            self.dirtyVars.add(name)
        elif not dirty and name in self.dirtyVars:
            self.dirtyVars.remove(name)
    
    def requestVar(self, name):
        networkServer.requestVar(self.name,name)
    
    def distSetVar(self, name, val):
        self.vars[name] = val
        networkServer.distSetVar(self.name,name,val)
        
    def batchSetVar(self, name, val):
        self.vars[name] = val
        networkServer.batchSetVar(self.name,name,val)
    
    def setVar(self, name, val):
        self.vars[name] = val
        networkServer.setVar(self.name,name,val)
        
    def getVar(self, name, default):
        self.requestVar(name)
        if name in self.vars:
            return self.vars[name]
        else:
            return default
    
    def hasVar(self, name):
        self.requestVar(name)
        return name in self.vars
    
    def defaultVar(self, name, val):
        if not self.hasVar(name):
            self.setVar(name,val)
        return self.getVar(name,val)
        
    def updateVars(self, d):
        for key, val in d.iteritems():
            self.setVar(key,val)
        
    def load_include(self, includes):
        if type(includes) == str:
            self.load(includes)
        elif type(includes) == list:
            map(self.load,includes)
        else:
            raise RuntimeError, "unknown include type %s" % type(includes)
            
    def load_vars(self, vars):
        for name, val in vars.iteritems():
            #self.setVar(name,val)
            self.distSetVar(name,val)
        
    def load_actions(self, actions):
        if type(actions) == list:
            map(self.runAction,actions)
        elif type(actions) == dict:
            for actionName, args in actions.iteritems():
                assert type(args) == dict
                if 'type' in args:
                    actionType = args.pop('type')
                    self.runAction(actionName,actionType,**args)
                else:
                    self.runAction(actionName, **args)
        
    @staticmethod
    def deleteObject(self):
        self._objects.pop(self.name)
        self.stopActions()
        networkServer.send("deleteObject",self.name)
        self.alive = False
        
    @staticmethod
    def hasObject(name):
        return name in Object._objects
    
    @staticmethod
    def getObject(name):
        assert Object.hasObject(name), "can't get unknown object %s" % name
        return Object._objects[name]
    
    @staticmethod
    def defaultMakeObject(name, **data):
        if Object.hasObject(name):
            return Object.getObject(name)
        else:
            return Object(name, **data)
        
    def delete(self):
        Object.deleteObject(self)
        
    @classmethod
    def deleteObjectByName(self, name):
        assert name in self._objects, "cannot delete unknown object %s" % name
        Object.deleteObject(self._objects[name])
        
    def stopActions(self):
        for action in self.actions:
            action.stop()
        
    def runAction(self, actionType, actionName = None, **args):
        realName = networkServer.runAction(self.name, actionType, actionName or actionType, args)
        return realName
        
    def doRunAction(self, actionType, actionName, args):
        actionClass = util.dynload(actionType)
        assert actionClass, "unknown action %s" % actionType
        assert issubclass(actionClass, Action), "actionType %s is not a subclass of Action" % actionType
        action = actionClass(self,actionName)
        self.actions[actionName] = action
        action(**args)
        return action
    
    def call(self, actionName, cmd, **args):
        if actionName in self.actions:
            assert cmd in self.actions[actionName].receiveFunctions
            return self.actions[actionName].receiveFunctions[cmd](args)
        else:
            return networkServer.rpc(self.name,actionName,cmd,args)
        
    def defer(self, actionName, cmd, **args):
        if actionName in self.actions:
            assert cmd in self.actions[actionName].receiveFunctions
            return lambda: self.actions[actionName].receiveFunctions[cmd](args)
        else:
            return networkServer.defer(self.name, actionName, cmd, args)
        
    @classmethod
    def search(self, cond):
        return filter(cond,self._objects.values())
    
    @classmethod
    def searchFirst(self, cond):
        for obj in copy(self._objects.itervalues()):
            if cond(obj):
                return obj
        return None
    
    def log(self, *v):
        print "%s: %s" % (str(self), ' '.join(map(str,v)))
        
from networkServer import networkServer
